home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Examples / qlipo / qlipo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-03  |  32.7 KB  |  1,414 lines

  1. /* 
  2.  * QLIPO -- NeXT compatible, portable LIPO. 
  3.  *
  4.  * You may not remove this comment header, though you may add to it.
  5.  * You may not remove attributions to the author or contributors, though
  6.  * you may add to them.
  7.  * 
  8.  * Darcy Brockbank <samurai@hasc.ca>
  9.  * Hutchison Ave. Software
  10.  */
  11. #define PROGRAM_NAME "qlipo"
  12. #define VERSION "Version 1.3."
  13. /*
  14.  * Version 1.3
  15.  *
  16.  * - using stat() to verify identical files, rather than strcmp() (duh.)
  17.  * Apr 3, 1995
  18.  *
  19.  * Version 1.2
  20.  *
  21.  * - allowed file-over-file overwrite specification
  22.  * - caught signals for cleanup
  23.  *
  24.  * Version 1.0 <BETA>
  25.  * Dec 22, 1994
  26.  * 
  27.  * This program is loosely based on plipo, by Christian Scheider, which
  28.  * served mainly as example code and inspiration that this could be done.
  29.  * The intent of this program is to furnish an upgrade path for people
  30.  * who have broken versions of 'lipo' as under NEXTSTEP 3.2 and previous
  31.  * versions. QLIPO is bug-for-bug compatible with lipo... well, not exactly
  32.  * bug-for-bug, but it does a good job of handling all the arguments you
  33.  * can pass it, while giving some more useful messages than our old
  34.  * buggy friend lipo.
  35.  * 
  36.  * For the most part, this program is portable to any UNIX, though it works
  37.  * only on MACH-O files. At the moment, I'm being very conservative by
  38.  * keeping the byte alignments of the fat files at 8192 byte boundaries. 
  39.  * While this will result in a bit of wasted space, it makes sure that
  40.  * all executables are happy. Future versions will allow for less
  41.  * conservative alignments.
  42.  * 
  43.  * This program is being provided with the hope it will be interesting,
  44.  * and perhaps useful, but no guarantees are made as to it's functionality
  45.  * or usefulness, nor do I or Hutchison Ave. Software warrant it in any
  46.  * other way.
  47.  * 
  48.  * Please keep me informed as to incompatabilities or problems, and I
  49.  * will do my best to fix things. Lots of this code is gross... I'm 
  50.  * sorry about that, but I'm just trying to get it to work. Typical
  51.  * C programmer's excuse. I did write this over the course of 24 hours
  52.  * though, so please be gentle with me.
  53.  *
  54.  * TODO
  55.  *
  56.  * 1) support segalign
  57.  * 2) support multiple -remove flags... I had no idea this could be
  58.  *    specified.
  59.  * 
  60.  */
  61.  
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include "cpu.h"
  66. #include <math.h>
  67. #include <errno.h>
  68. #include <sys/stat.h>
  69. #ifdef NeXT
  70. #  include <libc.h>
  71. #else
  72. #  include <unistd.h>
  73. #endif
  74. #include <signal.h>
  75.  
  76. #define FAT_MAGIC  0xcafebabe
  77. #define NON_FAT_BIG_ENDIAN     0xfeedface
  78. #define NON_FAT_LITTLE_ENDIAN  0xcefaedfe
  79. #define STRDUP(a) (strcpy((char *)malloc(strlen((a))+1)),a)
  80.  
  81. /* sizeof() will pad the struct */
  82. #define HEADER_BYTE_COUNT 8
  83. typedef struct fat_header 
  84. {
  85.     char magic[4];      /* FAT_MAGIC */
  86.     char nfat_arch[4];  /* number of structs that follow */
  87. } fat_header_t;
  88.  
  89. /* sizeof() will pad the struct */
  90. #define ARCH_BYTE_COUNT 20
  91. typedef struct fat_arch 
  92. {
  93.     char cputype[4];      /* cpu specifier (int) */
  94.     char cpusubtype[4];   /* machine specifier (int) */
  95.     char offset[4];       /* file offset to this object file */
  96.     char size[4];         /* size of this object file */
  97.     char align[4];        /* alignment as a power of 2 */
  98. } fat_arch_t;
  99.  
  100. typedef struct non_fat_header 
  101. {
  102.     char magic[4];      /* FAT_MAGIC */
  103.     char cputype[4];    /* cpu specifier (int) */
  104. } non_fat_header_t;
  105.  
  106. /* types of files */
  107. #define MH_OBJECT     0x1
  108. #define MH_EXECUTE    0x2
  109. #define    MH_FVMLIB    0x3
  110. #define MH_CORE        0x4
  111. #define MH_PRELOAD    0x5
  112. #define MH_DYLIB    0x6
  113. #define MH_DYLINKER    0x7
  114. #define MH_BUNDLE    0x8
  115.  
  116. /* flags */
  117. #define MH_NOUNDEFS    0x1
  118. #define MH_INCRLINK    0x2
  119. #define MH_DYLDLINK    0x4
  120.  
  121. #define LOADER_BYTE_COUNT 28
  122. typedef struct loader
  123. {
  124.     char magic[4];   /* magic number */
  125.     char cputype[4];   /* machine specifier (int) */
  126.     char cpusubtype[4];   /* machine specifier (int) */
  127.     char filetype[4];       /* the kind of file */
  128.     char ncmds[4];         /* size of all the load commands */
  129.     char sizeofcmds[4];         /* size of all the load commands */
  130.     char flags[4];        /* flags */
  131. } loader_t;
  132.  
  133. #define GET32(x) ((unsigned long)((((unsigned char *)(x))[0] << 24) | \
  134.                   (((unsigned char *)(x))[1] << 16) | \
  135.                   (((unsigned char *)(x))[2] <<  8) | \
  136.                   (((unsigned char *)(x))[3])))
  137.  
  138. #define GET32L(x) ((unsigned long)((((unsigned char *)(x))[3] << 24) | \
  139.                    (((unsigned char *)(x))[2] << 16) | \
  140.                    (((unsigned char *)(x))[1] <<  8) | \
  141.                    (((unsigned char *)(x))[0])))
  142.  
  143. #define SET32(a, x) ((((unsigned char *)(a))[0] = (unsigned char )(x >> 24)), \
  144.              (((unsigned char *)(a))[1] = (unsigned char )(x >> 16)), \
  145.              (((unsigned char *)(a))[2] = (unsigned char )(x >>  8)), \
  146.              (((unsigned char *)(a))[3]) = (unsigned char )x)
  147.  
  148. #define COPY32(d, s) (((char *)(d))[0] = ((char *)(s))[0]), \
  149.     (((char *)(d))[1] = ((char *)(s))[1]), \
  150.     (((char *)(d))[2] = ((char *)(s))[2]), \
  151.     (((char *)(d))[3] = ((char *)(s))[3]);
  152.  
  153.  
  154. #define IS_FAT(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC)
  155.  
  156. #define IS_MAGIC(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC || \
  157.     GET32(((hdr)->magic))==NON_FAT_BIG_ENDIAN || \
  158.     GET32(((hdr)->magic))==NON_FAT_LITTLE_ENDIAN)
  159.  
  160.     /* 2^13 == 8192 */
  161. #define DEFAULT_ALIGN 13
  162.     
  163. /* 
  164.  * one of the operations we can perform...
  165.  */
  166.  
  167. typedef enum {
  168.     _info=0,
  169.     _detailed_info,
  170.     _create,
  171.     _thin,
  172.     _replace,
  173.     _remove,
  174.     _extract,
  175.     _arch,    /* non unique */
  176.     _output,    /* non unique */
  177.     _segalign    /* non unique ... and ignored under this version */
  178. } operation_type_t;
  179.  
  180. typedef struct _operation_t {
  181.     operation_type_t op;
  182.     char * argv[3];
  183.     int argc;
  184. } operation_t;
  185.  
  186. /* 
  187.  * The switches we're looking for, and their matching arguments.
  188.  */
  189.  
  190. typedef struct _arg_t {
  191.     const char * name;
  192.     int numargs;
  193.     operation_type_t op;
  194. } arg_t;
  195.  
  196. /* 
  197.  * Argument parsing aid .
  198.  */
  199. const arg_t argument_set[] = {
  200.     {"-info",0,_info},
  201.     {"-detailed_info",0,_detailed_info},
  202.     {"-arch",2,_arch},    
  203.     {"-output",1,_output},
  204.     {"-o",1,_output},
  205.     {"-create",0,_create},
  206.     {"-thin",1,_thin},
  207.     {"-replace",2,_replace},
  208.     {"-remove",1,_remove},
  209.     {"-extract",1,_extract},
  210.     {"-segalign",2,_segalign},
  211.     {0}
  212. };
  213.  
  214. /* 
  215.  * Filename and association.
  216.  */
  217. typedef struct _filelist_t {
  218.     char * filename;    /* our name */
  219.     int cputype;    /* our cpu */
  220.     int cpusubtype;    /* and subtype */
  221.     off_t size;        /* our size */
  222.     long start;        /* which fat arch we own in the list */
  223.     int isfat;        /* are we fat? */
  224.     int skip;        /* an arch to skip */
  225.     int suck;        /* the only arch to keep */
  226.     int narch;        /* the number of architectures */
  227.     unsigned long mode; /* the permissions */
  228. } filelist_t;
  229.  
  230. /* 
  231.  * A list of named input files. Null terminated, and counted for your
  232.  * convenience.
  233.  */
  234. filelist_t ** input_files=0;
  235. int input_file_count=0;
  236.  
  237. /*
  238.  * An output file.
  239.  */
  240. filelist_t output_file={0,CPU_TYPE_ANY,0};
  241.  
  242. /*
  243.  * An overwrite file.
  244.  */
  245. static char * overwrite_file = 0;
  246.  
  247.  
  248. /*
  249.  * A buffer for everyone to play in.
  250.  */
  251. #define BUFFER_SIZE 8192
  252. static char buffer[BUFFER_SIZE];
  253.  
  254. #define DEBUG
  255. #ifdef DEBUG
  256. static int ArgC=0;
  257. static char ** ArgV=0;
  258. static void
  259. quit(int flag)
  260. {
  261.     if (flag){
  262.     int i;
  263.     fprintf(stderr,"ArgV: qlipo ");
  264.     for(i=1;i<ArgC;i++){
  265.         fprintf(stderr,"%s ",ArgV[i]);
  266.     }
  267.     fprintf(stderr,"\n");
  268.     }
  269.     if (overwrite_file) {
  270.     unlink(overwrite_file);
  271.     }
  272.     exit(flag);
  273. }
  274. #else
  275. #  define quit(flag) exit(flag)
  276. #endif
  277.  
  278.  
  279.         
  280. static void
  281. usage(void)
  282. {
  283.     fprintf(stderr,
  284.         "qlipo: NeXT compatible lipo, by Darcy Brockbank <samurai@hasc.ca>\n"
  285.         "       based on 'plipo' by Christian Schneider. " VERSION "\n"
  286.         "Note:  ONLY one of -create, -thin <arch_type>, -extract <arch_type>,\n"
  287.         "       -remove <arch_type>, -replace <arch_type> <file_name>, -info \n"
  288.         "       or -detailed_info must be specified.\n"
  289.         "Usage: qlipo [input_file] ... [-arch <arch_type> input_file] ...\n"
  290.         "       [-info] [-detailed_info] [-output output_file] [-create]\n"
  291.         "       [-thin <arch_type>] [-extract <arch_type] ... [-remove <arch_type>]\n"
  292.         "       ... [-replace <arch_type> <file_name>] ...\n");
  293.     quit(1);
  294. }
  295.  
  296. static void 
  297. stat_file(int which)
  298. {
  299.     const char * filename = input_files[which]->filename;
  300.     struct stat sb;
  301.     stat(filename,&sb);
  302.     input_files[which]->size = sb.st_size;
  303.     input_files[which]->mode = sb.st_mode;
  304. }
  305.  
  306. /*
  307.  * Returns a structure representing the switch in the arglist, and what
  308.  * it is. If it's nothing (ie. a filename) then returns NULL.
  309.  */
  310. const arg_t *
  311. switch_at(char *argv[],int i)
  312. {
  313.     const arg_t *op;
  314.     for(op=argument_set;op->name;op++){
  315.     if (strcmp(op->name,argv[i])==0){
  316.         return op;
  317.     }
  318.     }
  319.     return 0;
  320. }
  321.  
  322. /* 
  323.  * Could expand this to check for syntax, but not right now
  324.  */
  325.  
  326. int 
  327. switch_argc(const arg_t *op)
  328. {
  329.     if (!op) return 0;
  330.     return op->numargs;
  331. }
  332.  
  333. static void
  334. grok_fileinfo(void)
  335. {
  336.     FILE * file;
  337.     long i;
  338.     loader_t header;
  339.     if (!input_file_count){
  340.     fprintf(stderr,"%s: No input files specified.\n", PROGRAM_NAME);
  341.     usage();
  342.     }
  343.     for(i=0;i<input_file_count;i++){
  344.     file = fopen(input_files[i]->filename,"r");
  345.     if (!file){
  346.         fprintf(stderr,"%s: Can't open input file \"%s\", (%s)\n",
  347.             PROGRAM_NAME, input_files[i]->filename,strerror(errno));
  348.         quit(1);
  349.     }
  350.     if (fread(&header,1,LOADER_BYTE_COUNT,file)==LOADER_BYTE_COUNT){
  351.         switch(GET32(header.magic)){
  352.         case NON_FAT_BIG_ENDIAN:
  353.         input_files[i]->cputype=GET32(header.cputype);
  354.         input_files[i]->cpusubtype=GET32(header.cpusubtype);
  355.         input_files[i]->narch = 1;
  356.         break;
  357.         case NON_FAT_LITTLE_ENDIAN:
  358.         input_files[i]->cputype=GET32L(header.cputype);
  359.         input_files[i]->cpusubtype=GET32L(header.cpusubtype);
  360.         input_files[i]->narch = 1;
  361.         break;
  362.         case FAT_MAGIC:
  363.         input_files[i]->isfat=1;
  364.         input_files[i]->narch = GET32(((fat_header_t *)&header)->nfat_arch);
  365.         break;
  366.         default:
  367.         /* ignore it */
  368.         break;
  369.         }
  370.     }
  371.     stat_file(i);
  372.     fclose(file);
  373.     }
  374.     output_file.mode = input_files[0]->mode;
  375. }
  376.  
  377. /* 
  378.  * Find the operation. If we've already found it, then we should quit.
  379.  */
  380. static operation_t *
  381. grok_operation(int argc, char *argv[])
  382. {
  383.     int i;
  384.     const arg_t *found=0;
  385.     const arg_t *current=0;
  386.     int spot=0;
  387.     int numargs;
  388.     operation_t * perform = calloc(sizeof(operation_t),1);
  389.     for(i=1;i<argc;i++){
  390.     if ((current = switch_at(argv,i))){
  391.         switch (current->op){
  392.         case _segalign:
  393.         case _output:
  394.         case _arch:
  395.         /* ignore */
  396.         break;
  397.         default:
  398.         if (found){
  399.             usage();
  400.         } else {
  401.             found=current;
  402.             spot=i;
  403.         }
  404.         }
  405.     }
  406.     }
  407.     if (!found) usage();
  408.     perform->op = found->op;
  409.     numargs = switch_argc(found);
  410.     for(i=0;i<numargs;i++){
  411.     if (spot+i+1>=argc){
  412.         usage();
  413.     }
  414.     perform->argv[i]=argv[spot+i+1];
  415.     }
  416.     perform->argv[i]=0;
  417.     perform->argc=i;
  418.     return perform;
  419. }
  420.     
  421. static void 
  422. add_file(char *filename, long type)
  423. {
  424.     filelist_t * new = calloc(sizeof(filelist_t),1);
  425.     new->filename = filename;
  426.     new->cputype = type;
  427.     input_files[input_file_count++]=new;
  428. }
  429.  
  430. /*
  431.  * Input filenames stand alone, or are preceded with -arch
  432.  */
  433. static void
  434. grok_infiles(int argc, char *argv[])
  435. {
  436.     int i;
  437.     const arg_t *arg;
  438.     /* we may need to add one under a -replace operation */
  439.     input_files = calloc(sizeof(filelist_t *),argc+1); 
  440.     
  441.     for(i=1;i<argc;i+=switch_argc(arg)+1){
  442.     arg = switch_at(argv,i);
  443.     if (!arg || arg->op == _arch){
  444.         if ((arg && i>=argc-2) || i>=argc) usage();
  445.         add_file(argv[(i)+((arg)?2:0)],cpu_type((arg)?argv[i+1]:0));
  446.     }
  447.     }
  448. }
  449.     
  450. static void
  451. grok_outfile(int argc, char *argv[])
  452. {
  453.     int i;
  454.     for(i=1;i<argc;i++){
  455.     const arg_t *arg = switch_at(argv,i);
  456.     if (arg && arg->op == _output){
  457.         if (output_file.filename || i>=argc-1) usage();
  458.         output_file.filename=argv[i+1];
  459.     }
  460.     }
  461. }
  462.  
  463. static void
  464. grok_myargs(int argc, char *argv[])
  465. {
  466.     int i;
  467.     for(i=1;i<argc;i++){
  468.     if (strcmp(argv[i],"-help")==0 ||
  469.         strcmp(argv[i],"--help")==0 ||
  470.         strcmp(argv[i],"-version")==0 ||
  471.         strcmp(argv[i],"--version")==0 ||
  472.         strcmp(argv[i],"-v")==0 ||
  473.         strcmp(argv[i],"-h")==0)
  474.     {
  475.         usage();
  476.     }
  477.     }
  478. }
  479.     
  480.     /* 
  481.      * We want to find a unique operation, build an input filelist
  482.      * and figure out the output file. I could do it efficiently
  483.      * by building a hashtable, etc, but this is OK because the
  484.      * data set is so tiny.
  485.      */
  486. static operation_t *
  487. grok_args(int argc, char *argv[])
  488. {
  489.     operation_t *ret;
  490.     if (argc<=1) {
  491.     usage();
  492.     }
  493.     grok_myargs(argc,argv);
  494.     grok_infiles(argc,argv);
  495.     grok_outfile(argc,argv);
  496.     ret = grok_operation(argc,argv);
  497.     grok_fileinfo();
  498.     return ret;
  499. }
  500.  
  501. static fat_arch_t *
  502. fatgetarch(char *mem, int cpu)
  503. {
  504.     fat_header_t *fh = (fat_header_t *)mem;
  505.     fat_arch_t *fa;
  506.  
  507.     if (fh && (GET32(fh->magic) == FAT_MAGIC))
  508.     {
  509.     unsigned long num;
  510.     fa = (fat_arch_t *)(fh + 1);
  511.     
  512.     for (num = GET32(fh->nfat_arch); num > 0; num--, fa++)
  513.     {
  514.         if ((cpu == CPU_TYPE_ANY) || (GET32(fa->cputype) == cpu))
  515.         return fa;
  516.     }
  517.     }
  518.     return (fat_arch_t *)0;
  519. }
  520.  
  521. static unsigned long 
  522. fatgetoffset(char *mem, int cpu)
  523. {
  524.     fat_arch_t *fa, *fa0;
  525.  
  526.     if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
  527.     return GET32(fa->offset);
  528.     else
  529.     return 0;
  530. }
  531.  
  532. static unsigned long 
  533. fatgetsize(char *mem, int cpu)
  534. {
  535.     fat_arch_t *fa;
  536.   
  537.     if ((fa = fatgetarch(mem, cpu)))
  538.     return GET32(fa->size);
  539.     else
  540.     return 0;
  541. }
  542.  
  543. static void 
  544. fatsave(char *file, char *mem, int cpu)
  545. {
  546.     FILE *out;
  547.     fat_arch_t *fa, *fa0;
  548.   
  549.     if (file && ((out = fopen(file, "w")) != NULL))
  550.     {
  551.     if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
  552.     {
  553.         char num[4], msg[] = "\nExtracted with qlipo\n";
  554.         unsigned long t;
  555.       
  556.         fwrite(mem, 4, 1, out);
  557.         SET32(num, 1);
  558.         fwrite(num, sizeof(num), 1, out);
  559.         t = GET32(fa->offset);
  560.         COPY32(fa->offset, fa0->offset);
  561.         fwrite(fa, sizeof(fat_arch_t), (size_t)1, out);
  562.         SET32(fa->offset, t);
  563.         fwrite(msg, sizeof(msg), 1, out);
  564.         fwrite(mem, (size_t)(GET32(fa0->offset) - sizeof(msg) -
  565.                  sizeof(fat_arch_t) - 4 - 4), 1, out);
  566.         fwrite(mem + fatgetoffset(mem, cpu),
  567.            (size_t)fatgetsize(mem, cpu), 1, out);
  568.     }
  569.     
  570.     fclose(out);
  571.     }
  572. }
  573.  
  574. static char *
  575. fatload(char *filename)
  576. {
  577.     FILE *file = stdin;
  578.     char *mem = NULL;
  579.     long old, size = 16384;
  580.   
  581.     if (filename && (!strcmp(filename, "-") || (file = fopen(filename, "r"))))
  582.     {
  583.     for (mem = (char *)malloc((size_t)size), old = 0;
  584.          mem = (char *)realloc(mem, (size_t)size);
  585.          old += (size - old), size *= 2)
  586.     {
  587.         if (fread(mem + old, 1, (size_t)(size - old), file) < (size - old))
  588.         {
  589.         if (ferror(file))
  590.         {
  591.             free(mem);
  592.             mem = NULL;
  593.         }
  594.         break;
  595.         }
  596.     }
  597.     fclose(file);
  598.     }
  599.     return mem;
  600. }
  601.  
  602. static long
  603. type_from_header(non_fat_header_t *nfh)
  604. {
  605.     long ct = CPU_TYPE_ANY;
  606.     if (GET32(nfh->magic) == NON_FAT_BIG_ENDIAN)
  607.     {
  608.     ct = (int)GET32(nfh->cputype);
  609.     } else if (GET32(nfh->magic) == NON_FAT_LITTLE_ENDIAN)
  610.     {
  611.     ct = (int)GET32L(nfh->cputype);
  612.     }
  613.     return ct;
  614. }      
  615.  
  616. static long
  617. type_from_fat_arch(fat_arch_t *fa)
  618. {
  619.     return GET32(fa->cputype);
  620. }
  621.  
  622. static long
  623. byte_alignment(fat_arch_t *fa)
  624. {
  625.     return pow(2,GET32(fa->align));
  626. }
  627.  
  628. static void 
  629. fatanalyse(char *mem, const char *filename)
  630. {
  631.     fat_header_t *fh = (fat_header_t *)mem;
  632.     non_fat_header_t *nfh = (non_fat_header_t *)mem;
  633.     int ct = -1;
  634.  
  635.     if (fh)
  636.     {
  637.     if (GET32(fh->magic) == FAT_MAGIC)
  638.     {
  639.         long num = GET32(fh->nfat_arch);
  640.         fat_arch_t *fa = (fat_arch_t *)(fh + 1);
  641.     
  642.         printf("Architectures (%ld) in the fat file: \"%s\" are:", num, filename);
  643.         for ( ; num > 0; fa++, num--)
  644.         {
  645.         int ct = (int)GET32(fa->cputype);
  646.       
  647.         if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
  648.             ct = 1;
  649.         printf(" %s", cpus[ct + 1]);
  650.         }
  651.         printf("\n");
  652.     } else {
  653.         ct = type_from_header(nfh);
  654.         if (ct >= 0) {
  655.         printf("Non-fat file: \"%s\" is architecture (%d)", filename,ct);
  656.         if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
  657.             ct = 1;
  658.         printf(": %s\n", cpus[ct + 1]);
  659.         } else {
  660.         printf("unknown file type (magic: %08x)\n", (unsigned)GET32(fh->magic)); 
  661.         }
  662.     }
  663.     }
  664. }
  665.  
  666. static void 
  667. info(void)
  668. {
  669.     char * mem;
  670.     int i;
  671.     for(i=0;input_files[i];i++){
  672.     if ((mem = fatload(input_files[i]->filename)))
  673.     {
  674.         fatanalyse(mem,input_files[i]->filename);
  675.         free(mem);
  676.     }
  677.     }
  678. }
  679.  
  680. static void
  681. fatprint_header(fat_header_t *header, const char *filename)
  682. {
  683.     if (IS_FAT(header)){
  684.     printf("%s: Fat header in: \"%s\"\n"
  685.            "fat_magic 0x%lx\n"
  686.            "nfat_arch %lx\n",
  687.            PROGRAM_NAME,
  688.            filename,
  689.            GET32(header->magic),
  690.            GET32(header->nfat_arch));
  691.     } else {
  692.     printf("Input file \"%s\" is not a fat file.\n",filename);
  693.     }
  694. }
  695.  
  696. static void 
  697. fatprint_arch(fat_arch_t *arch)
  698. {
  699.     long type = GET32(arch->cputype);
  700.     long subtype = GET32(arch->cpusubtype);
  701.     const char * architecture = arch_from_type(type);
  702.     const char * cputype = get_cputype(type);
  703.     const char * cpusubtype = get_cpusubtype(type,subtype);
  704.     long offset = GET32(arch->offset);
  705.     long size = GET32(arch->size);
  706.     long align = GET32(arch->align);
  707.     printf("architecture %s\n"
  708.        "    cputype %s\n"
  709.        "    cpusubtype %s\n"
  710.        "    offset %ld\n"
  711.        "    size %ld\n"
  712.        "    align 2^%ld (%.0f)\n",
  713.        architecture,cputype,cpusubtype,offset,size,align,pow(2.0,(float)align));
  714. }
  715.     
  716. static void
  717. fatprint(const char *filename)
  718. {
  719.     FILE *file = fopen(filename,"r");
  720.     fat_header_t header;
  721.     fat_arch_t arch;
  722.     if (!file) {
  723.     fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
  724.         PROGRAM_NAME,
  725.         filename,strerror(errno));
  726.     quit(1);
  727.     }
  728.     fread(&header,1,HEADER_BYTE_COUNT,file);
  729.     fatprint_header(&header,filename);
  730.     if (IS_FAT(&header)){
  731.     long i,count=GET32(header.nfat_arch);
  732.     for(i=0;i<count;i++){
  733.         fread(&arch,1,ARCH_BYTE_COUNT,file);
  734.         fatprint_arch(&arch);
  735.     }
  736.     } else {
  737.     /* nfat_arch is filled with the cpu type if the file is not fat */
  738.     long type = type_from_header((non_fat_header_t *)&header);
  739.     printf("Non-fat file: \"%s\" is architecture: %s\n",
  740.            filename,
  741.            arch_from_type(type));
  742.     }
  743.     fclose(file);
  744. }
  745.  
  746.     
  747. static void 
  748. detailed_info(void)
  749. {
  750.     int i;
  751.     for(i=0;input_files[i];i++){
  752.     fatprint(input_files[i]->filename);
  753.     }
  754. }
  755.  
  756. static void 
  757. write_fat_header(FILE *file, long archs)
  758. {
  759.     fat_header_t header;
  760.     SET32(header.magic,FAT_MAGIC);
  761.     SET32(header.nfat_arch,archs);
  762.     if (fwrite(&header,1,HEADER_BYTE_COUNT,file)!=HEADER_BYTE_COUNT){
  763.     fprintf(stderr,"Error writing to file... (%s)\n",strerror(errno));
  764.     quit(1);
  765.     }
  766. }
  767.  
  768. static void 
  769. set_fat_arch(fat_arch_t *a,long ct, long cst, long o, long s, long algn)
  770. {
  771.     SET32(a->cputype,ct);
  772.     SET32(a->cpusubtype,cst);
  773.     SET32(a->offset,o);
  774.     SET32(a->size,s);
  775.     SET32(a->align,algn);
  776. }
  777.  
  778. static fat_arch_t *
  779. make_fat_arch_at(long which)
  780. {
  781.     long size = input_files[which]->size;
  782.     long offset = 0; /* we consider it pure data... */
  783.     long cputype = input_files[which]->cputype;
  784.     long cpusubtype = input_files[which]->cpusubtype;
  785.     long align = DEFAULT_ALIGN;    
  786.     fat_arch_t * a;
  787.     if (cputype == CPU_TYPE_ANY){
  788.     fprintf(stderr,"%s: Can't figure out the arch type for \"%s\".\n",
  789.         PROGRAM_NAME,input_files[which]->filename);
  790.     usage();
  791.     }
  792.     a = calloc(sizeof(fat_arch_t),1);
  793.     set_fat_arch(a,cputype,cpusubtype,offset,size,align);
  794.     return a;
  795. }
  796.  
  797.     
  798. static long 
  799. add_arch(fat_arch_t ***fa, fat_arch_t *add, long at)
  800. {
  801.     if (!*fa) {
  802.     *fa = calloc(sizeof(fat_arch_t *),1);
  803.     } else {
  804.     *fa = realloc(*fa, sizeof(fat_arch_t *)*(at+1));
  805.     }
  806.     (*fa)[at]=add;
  807.     return 1;
  808. }
  809.  
  810. static long
  811. load_fat_arch(long which, fat_arch_t ***fa, long total)
  812. {
  813.     FILE * file = fopen(input_files[which]->filename,"r");
  814.     fat_header_t header;
  815.     fat_arch_t *a;
  816.     if (!file) { /* Can't assume it's still here. */
  817.     fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
  818.         PROGRAM_NAME,
  819.         input_files[which]->filename,strerror(errno));
  820.     quit(1);
  821.     }
  822.     fread(&header,1,HEADER_BYTE_COUNT,file);
  823.     if (input_files[which]->isfat){
  824.     long i,count=GET32(header.nfat_arch);
  825.     for(i=0;i<count;i++){
  826.         a = calloc(sizeof(fat_arch_t),1);
  827.         fread(a,1,ARCH_BYTE_COUNT,file);
  828.         total += add_arch(fa,a,total);
  829.     }
  830.     } else {
  831.     a = make_fat_arch_at(which);
  832.     total += add_arch(fa,a,total);
  833.     }
  834.     fclose(file);
  835.     return total;
  836. }
  837.     
  838. static fat_arch_t **
  839. load_fat_archs(long *total)
  840. {
  841.     long i;
  842.     long count = 0;
  843.     fat_arch_t **fa = 0;
  844.     for(i=0;i<input_file_count;i++){
  845.     input_files[i]->start = count;
  846.     count = load_fat_arch(i,&fa,count);
  847.     }
  848.     *total = count;
  849.     return fa;
  850. }
  851.  
  852. static long
  853. file_owning_fa(long chunk)
  854. {
  855.     int i;
  856.     for(i=input_file_count-1;i>0;i--){
  857.     if (input_files[i]->start <= chunk){
  858.         return i;
  859.     }
  860.     }
  861.     return 0;
  862. }
  863.  
  864. static void 
  865. consistency_check(fat_arch_t **fa, long count)
  866. {
  867.     long archs[CPU_TYPE_MAX];
  868.     long i;
  869.     for(i=0;i<CPU_TYPE_MAX;i++) archs[i]=-1;
  870.     for(i=0;i<count;i++){
  871.     long current = type_from_fat_arch(fa[i]);
  872.     long which, suck, skip;
  873.     if (current<0 || current>=CPU_TYPE_MAX){
  874.         fprintf(stderr,"%s: File \"%s\" has unknown architecture.\n",
  875.             PROGRAM_NAME,input_files[file_owning_fa(i)]->filename);
  876.         quit(1);
  877.     }
  878.     which = file_owning_fa(i);
  879.     suck = input_files[which]->suck;
  880.     skip = input_files[which]->skip;
  881.     /* 
  882.      * See if this file is losing this architecture, or if it is the
  883.      * only one being preserved.
  884.      */
  885.     if (skip != current && (!suck || suck==current)){
  886.         if (archs[current]!=-1) {
  887.         fprintf(stderr,
  888.             "%s: \"%s\" and \"%s\" share common architecture (%s)\n" 
  889.             "       and can't exist in the same fat output file.\n",
  890.             PROGRAM_NAME,
  891.             input_files[archs[current]]->filename,
  892.             input_files[file_owning_fa(i)]->filename,
  893.             get_cputype(current));
  894.         quit(1);
  895.         }
  896.         archs[current] = which;
  897.     }
  898.     }
  899. }
  900.  
  901. static int
  902. files_are_identical(const char *f1, const char *f2)
  903. {
  904.     if (strcmp(f1,f2)==0){
  905.     return 1;
  906.     } else {
  907.     struct stat sb1, sb2;
  908.     if (stat(f1,&sb1)){
  909.         fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
  910.             PROGRAM_NAME, f1,strerror(errno));
  911.         quit(1);
  912.     }
  913.     if (stat(f2,&sb2)){
  914.         fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
  915.             PROGRAM_NAME, f1,strerror(errno));
  916.         quit(1);
  917.     }
  918.     return (sb1.st_ino == sb2.st_ino);
  919.     }
  920. }
  921.  
  922. static FILE *
  923. muddle_outfile(void)
  924. {
  925.     int i;
  926.     FILE * file = 0;
  927.     for(i=0;i<input_file_count;i++){
  928.     if (files_are_identical(output_file.filename,input_files[i]->filename)){
  929.         overwrite_file = malloc(MAXPATHLEN);
  930.         sprintf(overwrite_file,"/tmp/%s.%d",PROGRAM_NAME,getpid());
  931.         file = (fopen(overwrite_file,"w"));
  932.     }
  933.     }
  934.     if (!file) {
  935.     file = fopen(output_file.filename,"w");
  936.     }
  937.     if (!(file)){
  938.     fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
  939.         PROGRAM_NAME, overwrite_file ? overwrite_file : 
  940.         output_file.filename,strerror(errno));
  941.     quit(1);
  942.     } 
  943.     return file;
  944. }
  945.  
  946. static void
  947. copy_filename(const char *dest, const char *source)
  948. {
  949.     char tmp[strlen(dest)+strlen(source)+128];
  950.     sprintf(tmp,"cp %s %s",source,dest);
  951.     system(tmp);
  952. }
  953.  
  954. static void
  955. close_output(FILE *file)
  956. {
  957.     FILE * source, *output;
  958.     fclose(file);
  959.     if (overwrite_file){
  960.     output = fopen(output_file.filename,"w");
  961.     if (!(output)){
  962.         fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
  963.         PROGRAM_NAME, output_file.filename,strerror(errno));
  964.         quit(1);
  965.     }
  966.     source = fopen(overwrite_file,"r");
  967.     if (!(source)){
  968.         fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
  969.         PROGRAM_NAME, overwrite_file,strerror(errno));
  970.         quit(1);
  971.     }
  972.     fclose(source);
  973.     fclose(output);
  974.     copy_filename(output_file.filename,overwrite_file);
  975.     } 
  976.     chmod(output_file.filename,output_file.mode);
  977. }
  978.  
  979. static FILE *
  980. grok_output(void)
  981. {
  982.     FILE * file;
  983.     if (!output_file.filename) 
  984.     {
  985.         fprintf(stderr,"%s: No output file specified.\n",PROGRAM_NAME);
  986.         usage();
  987.     }
  988.     file = muddle_outfile();
  989.     return file;
  990. }
  991.  
  992.     
  993. static void
  994. write_fat_archlist(FILE *output, fat_arch_t **fa, long total)
  995. {
  996.     long offset=0;
  997.     long align,j,i,stop;
  998.     long old_offset;
  999.     long skip;
  1000.     long suck;
  1001.     for(i=0;i<input_file_count;i++){
  1002.     if (i<input_file_count-1){
  1003.         stop = input_files[i+1]->start;
  1004.     } else {
  1005.         stop = total;
  1006.     }
  1007.     skip = input_files[i]->skip;
  1008.     suck = input_files[i]->suck;
  1009.     for(j=input_files[i]->start;j<stop;j++){
  1010.         long current = GET32(fa[j]->cputype);
  1011.         if (current != skip){
  1012.         if (current == suck || !suck){
  1013.             align = byte_alignment(fa[j]);
  1014.             offset = ((offset / align) + 1) * align;
  1015.             old_offset = GET32(fa[j]->offset);
  1016.             SET32(fa[j]->offset,offset);
  1017.             fwrite(fa[j],1,ARCH_BYTE_COUNT,output);
  1018.             offset+=GET32(fa[j]->size);
  1019.             /* we set it back so we know were to get it later */
  1020.             SET32(fa[j]->offset,old_offset);
  1021.         }
  1022.         }
  1023.     }
  1024.     }
  1025. }
  1026.  
  1027. static void
  1028. write_pad(FILE *output, long start, long align)
  1029. {
  1030.     long count = (align) ? align - start % align : 0;
  1031.     long i;
  1032.     char null='\0';
  1033.     for(i=0;i<count;i++){
  1034.     fwrite(&null,1,1,output);
  1035.     }
  1036. }
  1037.  
  1038. static void 
  1039. write_binary(FILE *output, long align, long which, long where, long size)
  1040. {
  1041.     long tell = ftell(output);
  1042.     /* assume it's still ok */
  1043.     FILE * source = fopen(input_files[which]->filename,"r"); 
  1044.     long bufsiz;
  1045.     long current;
  1046.     write_pad(output,tell,align);
  1047.     fseek(source,where,SEEK_SET);
  1048.     bufsiz = (BUFFER_SIZE > input_files[which]->size) ?
  1049.     input_files[which]->size : BUFFER_SIZE;
  1050.     
  1051.     for(current=bufsiz;(size>0);){
  1052.     if (fread(buffer,1,current,source)!=current){
  1053.         fprintf(stderr,"%s: Error reading from \"%s\", (%s)\n",
  1054.             PROGRAM_NAME,input_files[which]->filename,strerror(errno));
  1055.     }
  1056.     if (fwrite(buffer,1,current,output)!=current){
  1057.         fprintf(stderr,"%s: Error writing to \"%s\", (%s)\n",
  1058.             PROGRAM_NAME,output_file.filename,strerror(errno));
  1059.     }
  1060.     size -= current;
  1061.     if (size<bufsiz){
  1062.         current = size;
  1063.     }
  1064.     }
  1065.     fclose(source);
  1066. }
  1067.  
  1068. static void
  1069. write_fat_binaries(FILE *output, fat_arch_t **fa, long total)
  1070. {
  1071.     long offset=0;
  1072.     long align,j,i,stop;
  1073.     long where,size;
  1074.     long skip, suck;
  1075.     for(i=0;i<input_file_count;i++){
  1076.     if (i<input_file_count-1){
  1077.         stop = input_files[i+1]->start;
  1078.     } else {
  1079.         stop = total;
  1080.     }
  1081.     skip = input_files[i]->skip;
  1082.     suck = input_files[i]->suck;
  1083.     for(j=input_files[i]->start;j<stop;j++){
  1084.         long current = GET32(fa[j]->cputype);
  1085.         if (current != skip){
  1086.         if (current == suck || !suck){
  1087.             align = byte_alignment(fa[j]);
  1088.             offset = ((offset / align) + 1) * align;
  1089.             where = GET32(fa[j]->offset);
  1090.             size = GET32(fa[j]->size);
  1091.             write_binary(output,align,i,where,size);
  1092.             offset+=size;
  1093.         }
  1094.         }
  1095.     }
  1096.     }
  1097. }    
  1098.     
  1099. /* take the infiles, and meld them into the outfile */
  1100. static void
  1101. create(const operation_t *perform)
  1102. {
  1103.     FILE * output;
  1104.     long total;
  1105.     fat_arch_t **fa;
  1106.     fa = load_fat_archs(&total);
  1107.     consistency_check(fa,total);
  1108.     output = grok_output();
  1109.     write_fat_header(output,total);
  1110.     write_fat_archlist(output,fa,total);
  1111.     write_fat_binaries(output,fa,total);
  1112.     close_output(output);
  1113. }
  1114.  
  1115. static fat_arch_t *
  1116. find_fat_arch(fat_arch_t **fa, long count, int type)
  1117. {
  1118.     long i;
  1119.     for(i=0;i<count;i++){
  1120.     if (GET32(fa[i]->cputype)==type){
  1121.         return fa[i];
  1122.     }
  1123.     }
  1124.     return 0;
  1125. }
  1126.  
  1127. static void
  1128. assert_one_input(const char *tag)
  1129. {
  1130.     if (input_file_count!=1){
  1131.     fprintf(stderr,"%s: %s requires that you specify a single "
  1132.         "input file.\n", PROGRAM_NAME, tag);
  1133.     usage();
  1134.     }
  1135. }
  1136.  
  1137. static void
  1138. assert_arch_arg(const operation_t *perform, const char *tag)
  1139. {
  1140.     if (perform->argc<1 || cpu_type(perform->argv[0])<=CPU_TYPE_ANY){
  1141.     fprintf(stderr,    "%s: %s requires an architecture argument, "
  1142.         "like \"m68k\"\n", PROGRAM_NAME,tag);
  1143.     usage();
  1144.     }
  1145. }
  1146.  
  1147. static void
  1148. assert_fat(int which, const char *tag)
  1149. {
  1150.     if (!input_files[which]->isfat){
  1151.     fprintf(stderr,"%s: \"%s\" is not fat, and %s only operates "
  1152.         "on fat files.\n",PROGRAM_NAME,input_files[which]->filename,tag);
  1153.     quit(1);
  1154.     }
  1155. }
  1156.  
  1157. static void
  1158. assert_arch(fat_arch_t *single, long type, const char *tag)
  1159. {
  1160.     if (!single){
  1161.     fprintf(stderr,
  1162.         "%s: \"%s\" doesn't contain the architecture \"%s\" passed "
  1163.         "to %s.\n",
  1164.         PROGRAM_NAME,input_files[0]->filename,get_cputype(type),tag);
  1165.     quit(1);
  1166.     }
  1167. }    
  1168.  
  1169. /* 
  1170.  * Take the infiles, and meld them into a fat outfile with one arch 
  1171.  */
  1172. static void
  1173. extract(const operation_t *perform)
  1174. {
  1175.     int type;
  1176.     FILE * output;
  1177.     long total;
  1178.     fat_arch_t **fa;
  1179.     fat_arch_t *single;
  1180.     
  1181.     assert_arch_arg(perform,"-extract");
  1182.     type = cpu_type(perform->argv[0]);
  1183.     assert_one_input("-extract");
  1184.     fa = load_fat_archs(&total);
  1185.     assert_fat(0,"-extract");
  1186.     consistency_check(fa,total);
  1187.     single = find_fat_arch(fa,total,type);
  1188.     assert_arch(single,type,"-extract");
  1189.     output = grok_output();
  1190.     write_fat_header(output,1);
  1191.     write_fat_archlist(output,&single,1);
  1192.     write_fat_binaries(output,&single,1);
  1193.     close_output(output);
  1194. }
  1195.  
  1196. /* 
  1197.  * Take the infile, and meld it into a thin outfile
  1198.  */
  1199. static void
  1200. thin(const operation_t *perform)
  1201. {
  1202.     FILE * output;
  1203.     long total;
  1204.     fat_arch_t **fa, *single;
  1205.     int type;
  1206.     
  1207.     assert_arch_arg(perform,"-thin");
  1208.     type = cpu_type(perform->argv[0]);
  1209.     assert_one_input("-thin");
  1210.     fa = load_fat_archs(&total);
  1211.     assert_fat(0,"-thin");
  1212.     consistency_check(fa,total);
  1213.     single = find_fat_arch(fa,total,type);
  1214.     assert_arch(single,type,"-thin");
  1215.     output = grok_output();
  1216.     write_binary(output,0,0,GET32(single->offset),GET32(single->size));
  1217.     close_output(output);
  1218. }
  1219.  
  1220. static void
  1221. assert_file_arg(const operation_t *perform, int index, const char *tag)
  1222. {
  1223.     if (perform->argc<=index){
  1224.     fprintf(stderr,"%s: %s requires more arguments than was given (%d)\n",
  1225.         PROGRAM_NAME,tag,index);
  1226.     usage();
  1227.     }
  1228. }
  1229.  
  1230. /* 
  1231.  * Take the infile, and replace the named arch with the named arch
  1232.  * from the argument file.
  1233.  */
  1234. static void
  1235. replace(const operation_t *perform)
  1236. {
  1237.     FILE * output;
  1238.     long total;
  1239.     fat_arch_t **fa, *single;
  1240.     int type;
  1241.     
  1242.     assert_one_input("-replace");
  1243.     assert_fat(0,"-replace");
  1244.     assert_arch_arg(perform,"-replace");
  1245.     type = cpu_type(perform->argv[0]);
  1246.     assert_file_arg(perform,1,"-replace");
  1247.     add_file(perform->argv[1],CPU_TYPE_ANY);
  1248.     /* 
  1249.      * Rerun this to assert the new file we just added.
  1250.      */
  1251.     grok_fileinfo();
  1252.     /*
  1253.      * file 0 is the source file, file 1 is the suck file
  1254.      */
  1255.     input_files[0]->skip = type;
  1256.     input_files[1]->suck = type;
  1257.     fa = load_fat_archs(&total);
  1258.     consistency_check(fa,total);
  1259.     single = find_fat_arch(fa,total,type);
  1260.     /* this is wrong... we have to check both files */
  1261.     assert_arch(single,type,"-replace");
  1262.     output = grok_output();
  1263.     write_fat_header(output,input_files[0]->narch);
  1264.     write_fat_archlist(output,fa,total);
  1265.     write_fat_binaries(output,fa,total);
  1266.     close_output(output);
  1267. }
  1268.  
  1269. static void
  1270. assert_total(int archcount, const char *tag)
  1271. {
  1272.     if (!archcount){
  1273.     fprintf(stderr,    "%s: %s operation would result in an empty fat file\n",
  1274.          PROGRAM_NAME,tag);
  1275.     quit(1);
  1276.     }
  1277. }
  1278.  
  1279. /* 
  1280.  * Remove the named architecture.
  1281.  */
  1282. static void
  1283. remove_arch(const operation_t *perform)
  1284. {
  1285.     FILE * output;
  1286.     long total;
  1287.     fat_arch_t **fa, *single;
  1288.     int type;
  1289.     
  1290.     assert_arch_arg(perform,"-remove");
  1291.     type = cpu_type(perform->argv[0]);
  1292.     
  1293.     assert_one_input("-remove");
  1294.     fa = load_fat_archs(&total);
  1295.     assert_fat(0,"-remove");
  1296.     consistency_check(fa,total);
  1297.     single = find_fat_arch(fa,total,type);
  1298.     assert_arch(single,type,"-remove");
  1299.     output = grok_output();
  1300.     assert_total(total-1,"-remove");
  1301.     write_fat_header(output,total-1);
  1302.     input_files[0]->skip = type;
  1303.     write_fat_archlist(output,fa,total);
  1304.     write_fat_binaries(output,fa,total);
  1305.     close_output(output);
  1306. }
  1307.  
  1308. void 
  1309. handler(int sig)
  1310. {
  1311.     const char *die=0;
  1312.     switch(sig)
  1313.     {
  1314.     case SIGINT:
  1315.     die = PROGRAM_NAME ": Caught an INTERRUPT. Bye.\n";
  1316.     break;
  1317.     case SIGHUP:
  1318.     die = PROGRAM_NAME ": Caught a HANGUP. That's rude! Bye.\n";
  1319.     break;
  1320.     case SIGTERM:
  1321.     die = PROGRAM_NAME ": Caught a SIGTERM. Bye.\n";
  1322.     break;
  1323.     }
  1324.     if (die){
  1325.     fprintf(stderr,"%s",die);
  1326.     quit(1);
  1327.     }
  1328. }
  1329.  
  1330. static void 
  1331. setup_signals(void)
  1332. {
  1333.     signal(SIGINT,handler);
  1334.     signal(SIGHUP,handler);
  1335.     signal(SIGTERM,handler);
  1336. }
  1337.  
  1338. int 
  1339. main(int argc, char **argv)
  1340. {
  1341.     int i, xcpu = -1;
  1342.     char *mem;
  1343.     extern int optind;
  1344.     operation_t * perform;
  1345. #ifdef DEBUG
  1346.     ArgC = argc;
  1347.     ArgV = argv;
  1348. #endif
  1349.     setup_signals();
  1350.     perform = grok_args(argc,argv);
  1351.     switch(perform->op){
  1352.     case _info:
  1353.     info();
  1354.     break;
  1355.     case _detailed_info:
  1356.     detailed_info();
  1357.     break;
  1358.     case _create:
  1359.     create(perform);
  1360.     break;
  1361.     case _extract:
  1362.     extract(perform);
  1363.     break;
  1364.     case _thin:
  1365.     thin(perform);
  1366.     break;
  1367.     case _replace:
  1368.     replace(perform);
  1369.     break;
  1370.     case _remove:
  1371.     remove_arch(perform);
  1372.     break;
  1373.     default:    /* arch, output, segalign */
  1374.     usage();
  1375.     break;
  1376.     }
  1377.  
  1378.     quit(0);
  1379.  
  1380.  
  1381.     if (xcpu > 0)
  1382.     printf("Extracting cpu type '%s'\n", cpus[xcpu + 1]);
  1383.  
  1384.     if (optind == argc)
  1385.     {
  1386.     }
  1387.  
  1388.     for (i = optind; i < argc; i++)
  1389.     {
  1390.     if ((mem = fatload(argv[i])))
  1391.     {
  1392.         char buf[1024];
  1393.  
  1394.         printf("File '%s': ", argv[i]);
  1395.         fatanalyse(mem,0);
  1396.  
  1397.         if (xcpu > 0)
  1398.         {
  1399.         if (fatgetarch(mem, xcpu))
  1400.         {
  1401.             sprintf(buf, "%s.%s", argv[i], cpus[xcpu + 1]);
  1402.             fatsave(buf, mem, xcpu);
  1403.         }
  1404.         else
  1405.             printf("specified architecture not found.\n");
  1406.         }
  1407.  
  1408.         free(mem);
  1409.     }
  1410.     }
  1411.  
  1412.     return 0;
  1413. }
  1414.